#ifndef _ARCHIVE_H
#define _ARCHIVE_H

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <dirent.h>
#include <libgen.h>

#include "bitstream.h"
#include "cHuffmanTree.h"
#include "lz77.h"


#define ARCHI_BSTR_SIZE		(10)
#define ARCHI_BLOCK_SIZE	(1<<12)

#define ARCHI_WINDOW_SIZE	(4*1024)
#define ARCHI_BUFFER_SIZE	(255)


enum archi_op
{
  archi_op_nop,
  archi_op_create,
  archi_op_extract
};

#define TYPE_DEFLATE		0

#define MODE_RESET_TREES	0
#define MODE_RESET_LZ77		1

struct archi_header
{
  uint8_t type; /* reserved for future use - this filed allows to develop other algorithms */
  uint8_t mode;	
  uint16_t window_size; /**/
  uint16_t buffer_size;
};

#define ARCHI_HEADER_DEFAULT {0,ARCHI_WINDOW_SIZE,ARCHI_BUFFER_SIZE}

struct archi_dirent_header
{
  mode_t 	mode;
  union
  {
    uint16_t 	name_len;
    uint16_t 	ctl;
  } name_len_ctl;
};

#define ARCHI_CTL_NOP		0
#define ARCHI_CTL_DIREND	1
#define ARCHI_CTL_END		2

class archi
{
  public:
    archi(bool nquiet);
    ~archi();
    void set_op(enum archi_op);
    enum archi_op get_op();
    void encode(char * f_in[],int n,char * f_out,uint16_t window_size,uint16_t buffer_size,bool reset);
    void encode_dir(char * f_in);
    void encode_file(FILE * fh);
    void decode(char * f_in,char * f_out);
    bool decode_dir(char * f_in);
    void decode_file(FILE * fh);
    void set_defaults();
    void set_file_in(char * fname);
    void set_file_out(char * fname);
  private:
    uint8_t get_symbol_size(uint16_t maxval);
    void print_ok();
    bool print_info;
    void encode_archi(bool inc_new = true);
    void decode_archi();
    void write_file_header(mode_t mode,char * name);
    void write_header();
    void read_header();
    void s_mkdir(const char * path,mode_t mode);
    void s_chdir(const char * path);
    uint16_t get_ctl(struct archi_dirent_header * adh);
    void write_ctl(uint16_t ctl,bool inc_new = true);
    void read_file_header(struct archi_dirent_header * adh);
    void dir_up();
    void encode_lz77_code(lz77_code * code);
    void clear_trees();
    void set_files(char * f_in,char * f_out);

    void release_file_in();
    void release_file_out();
    void release_files();
    
    struct archi_header archi_header;
    char * fname_in;
    char * fname_out; 
    FILE * fh_out;
    FILE * fh_in;
    enum archi_op op;
    bool reset_trees;
    bool reset_lz77;
    cLZ77 * lz77;
    cHuffmanTree<uint16_t> * ht_back_dist;
    cHuffmanTree<uint8_t> * ht_length;
    cHuffmanTree<uint8_t> * ht_literal;
    cHuffmanTree<uint8_t> * ht_headers;
    cHuffmanTree<uint8_t> * ht_names;
    cBitstream * bstr;
};


#endif //_ARCHIVE_H